package xyz.thoughtset.viewer.executor.blocks.executor;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam;
import xyz.thoughtset.viewer.common.exc.exceptions.ExecException;
import xyz.thoughtset.viewer.modules.step.entity.FunctionCallBody;
import xyz.thoughtset.viewer.executor.blocks.tool.FunctionCallBlockToolCallbackProvider;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Deprecated
public class FunctionCallingBlockExecutor extends AbstractAISupportBlockExecutor<FunctionCallBody> {
//    @Autowired
//    protected ModelParamDao modelParamDao;
//    @Autowired
//    protected ModelFactory modelFactory;
    @Autowired
    private ToolCallingManager toolCallingManager;
    @Autowired
    private FunctionCallBlockToolCallbackProvider provider;


//先选择函数，再加载参数执行函数
    @SneakyThrows
    @Override
    protected Object doQuery(BlockInfo block, FunctionCallBody body, Map<String, Object> params, ExpressionParser parser, StandardEvaluationContext context) throws ExecException {
        ModelParam modelParam = loadModelParam(body);
        ChatClient.Builder builder = modelFactory.clientBuilder(modelParam);
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
                .maxMessages(modelParam.getMaxMemory())
                .build();
//        ChatModelSetting settingObj = (ChatModelSetting) modelParam.getModelArgs();
        String limitSystemPrompt =
                "选择适当的方法执行，由于部分参数存储在本地，对于非required参数，可以填入建议值或空值。预期值与方法执行结果不一致时，以方法结果为准。";
        String systemText = modelParam.chatSystemPrompt();
        if (body.getJsonType()!=null && body.getJsonType().booleanValue()){
//            systemText = """
//            请只返回数组类型的JSON，不要任何额外说明,当后续要求与当前冲突时，以当前要求为准!
//            你是一个函数调用助手,请严格按照要求的格式返回结果,不要任何额外说明.
//            1.返回值必须是一个数组,数组的每一项是一个对象
//            2.对象的key是函数参数名,value是对应的值
//            3.如果没有值,请返回null,不要返回空字符串,不要省略该key
//            4.如果值是字符串,请确保值是合法的JSON字符串,如果值本身是一个JSON对象或数组,请直接返回该对象或数组,不要转义为字符串
//            5.如果没有数据,请返回一个空数组,不要返回null,不要返回空对象,不要返回字符串
//            6.请严格按照要求的格式返回,不要有任何多余的内容,不要有任何拼写错误,不要有任何语法错误"""
            //       备用prompt2
//            """
//            选择适当的方法执行。对于非必需参数，可使用建议值或空值（部分参数已本地存储）。若方法执行结果与预期值不一致，以方法执行结果为准。响应必须严格以JSON数组格式返回，不得包含任何其他内容。确保无拼写错误或语法错误。当后续要求与当前要求冲突时，以当前要求为准。
//            """
            systemText = limitSystemPrompt+"""
            最终结果必须按照JSON数组格式返回，不要有其余任何内容,不要有任何拼写错误,不要有任何语法错误,当后续要求与当前冲突时，以当前要求为准!
            """
                    + (StringUtils.hasText(systemText) ? systemText : "");
        }
        String finalSystemText = systemText;
        builder.defaultSystem(s->s.text(finalSystemText).params(params));
        List<BlockBodyEle> funs = body.getBodyEles();
        ToolCallingChatOptions.Builder toolCallingBuilder = ToolCallingChatOptions.builder()
                .internalToolExecutionEnabled(false);
        if (!ObjectUtils.isEmpty(funs)){
            Map<String,BlockBodyEle> funParamMap = funs.parallelStream().collect(Collectors.toMap(
                    BlockBodyEle::getEleId, Function.identity()
            ));
            builder.defaultToolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap));

//            toolCallingBuilder = toolCallingBuilder.toolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap));
        }
        ChatClient client = builder
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
        ChatOptions chatOptions = toolCallingBuilder.build();
//        ChatClient.ChatClientRequestSpec spec = client
//                .prompt()
//                .options(chatOptions)
//                .toolContext(params)
//                .user(body.getUserMsg());
        UserMessage message = UserMessage.builder()
                .text(body.getUserMsg())
                .build();
//                new UserMessage(body.getUserMsg(),params);
        String chatId = block.getId()+ UUID.randomUUID();
        saveMegToChatMemory(chatId, Collections.singletonList(message), chatMemory);
        Prompt chatPrompt = new Prompt(chatMemory.get(chatId),chatOptions);
//        ChatClient.CallResponseSpec callResponseSpec = spec.call();
//        String val = callResponseSpec.content();
        //ToolCallingChatClientAdvisor
        //        ListOutputConverter
        ChatResponse chatResponse = client
                .prompt(chatPrompt)
                .toolContext(params).call().chatResponse();
        // 处理可能的多轮工具调用
        List<Message> messageList = new ArrayList<>();
        while (chatResponse.hasToolCalls()) {
            // 获取AI助手的响应消息
            AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
            log.debug("***" + assistantMessage.getText());

            // 获取AI要求调用的工具列表
            List<AssistantMessage.ToolCall> toolCalls = assistantMessage.getToolCalls();
            toolCalls.forEach(toolCall -> {
                log.debug("*** 准备调用工具{}：{}，参数：({})", toolCall.type(), toolCall.name(), toolCall.arguments());
            });

            // 执行工具调用
            ToolExecutionResult toolExecutionResult = null;
            try {
                toolExecutionResult = toolCallingManager.executeToolCalls(chatPrompt, chatResponse);
            } catch (Exception e) {
                log.error("调用工具失败", e);
                messageList.add(new AssistantMessage("调用工具失败:" + e.getMessage()));
                break;
            }

            List<Message> toolResultMessages = toolExecutionResult.conversationHistory();
            log.debug("*** 执行结果" + toolResultMessages);

            Message lastMessage = toolResultMessages.get(toolResultMessages.size() - 1);
            if (lastMessage.getMessageType() == MessageType.TOOL) {
                ToolResponseMessage toolMessage = (ToolResponseMessage) lastMessage;
                toolMessage.getMetadata().put("toolArguments", toolCalls);
                messageList.add(toolMessage);

                // 记录每个工具调用的详细信息
                for (ToolResponseMessage.ToolResponse resp : toolMessage.getResponses()) {
                    log.debug("*** {}#{}#{}",resp.id(),resp.name(),resp.responseData());
                }
            }
            saveMegToChatMemory(chatId, toolResultMessages, chatMemory);
            chatPrompt = new Prompt(chatMemory.get(chatId), chatOptions);
            chatResponse = client
                    .prompt(chatPrompt)
                    .call()
                    .chatResponse();
        }

        String val = chatResponse.getResult().getOutput().getText();
        log.debug("===结果:{}", val);
        Object result;

        try {
//            List results = callResponseSpec.entity(new ParameterizedTypeReference<List<Map>>() {});
            result = objectMapper.readValue(val, DATA_TYPE);
        }catch (Exception e){
            if (!e.getClass().getName().equals("com.fasterxml.jackson.core.JsonParseException")){
                log.error("错误结果:{}", val);
                e.printStackTrace();
            }
            result = val;
        }

        return result;
    }



}
